home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / ai_team.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  23.2 KB  |  784 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3.  
  4. /*****************************************************************************
  5.  * name:        ai_team.c
  6.  *
  7.  * desc:        Quake3 bot AI
  8.  *
  9.  * $Archive: /source/code/game/ai_team.c $
  10.  * $Author: Raduffy $ 
  11.  * $Revision: 5 $
  12.  * $Modtime: 1/14/00 5:28p $
  13.  * $Date: 1/14/00 5:35p $
  14.  *
  15.  *****************************************************************************/
  16.  
  17. #include "g_local.h"
  18. #include "botlib.h"
  19. #include "be_aas.h"
  20. #include "be_ea.h"
  21. #include "be_ai_char.h"
  22. #include "be_ai_chat.h"
  23. #include "be_ai_gen.h"
  24. #include "be_ai_goal.h"
  25. #include "be_ai_move.h"
  26. #include "be_ai_weap.h"
  27. //
  28. #include "ai_main.h"
  29. #include "ai_dmq3.h"
  30. #include "ai_chat.h"
  31. #include "ai_cmd.h"
  32. #include "ai_dmnet.h"
  33.  
  34. //ctf task preferences for a client
  35. typedef struct bot_ctftaskpreference_s
  36. {
  37.     char        name[36];
  38.     int            preference;
  39. } bot_ctftaskpreference_t;
  40.  
  41. bot_ctftaskpreference_t ctftaskpreferences[MAX_CLIENTS];
  42.  
  43.  
  44. /*
  45. ==================
  46. BotValidTeamLeader
  47. ==================
  48. */
  49. int BotValidTeamLeader(bot_state_t *bs) {
  50.     if (!strlen(bs->teamleader)) return qfalse;
  51.     if (ClientFromName(bs->teamleader) == -1) return qfalse;
  52.     return qtrue;
  53. }
  54.  
  55. /*
  56. ==================
  57. BotNumTeamMates
  58. ==================
  59. */
  60. int BotNumTeamMates(bot_state_t *bs) {
  61.     int i, numplayers;
  62.     char buf[MAX_INFO_STRING];
  63.     static int maxclients;
  64.  
  65.     if (!maxclients)
  66.         maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
  67.  
  68.     numplayers = 0;
  69.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  70.         trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
  71.         //if no config string or no name
  72.         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
  73.         //skip spectators
  74.         if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
  75.         //
  76.         if (BotSameTeam(bs, i)) {
  77.             numplayers++;
  78.         }
  79.     }
  80.     return numplayers;
  81. }
  82.  
  83. /*
  84. ==================
  85. BotClientTravelTimeToGoal
  86. ==================
  87. */
  88. int BotClientTravelTimeToGoal(int client, bot_goal_t *goal) {
  89.     playerState_t ps;
  90.     int areanum;
  91.  
  92.     BotAI_GetClientState(client, &ps);
  93.     areanum = BotPointAreaNum(ps.origin);
  94.     if (!areanum) return 1;
  95.     return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT);
  96. }
  97.  
  98. /*
  99. ==================
  100. BotSortTeamMatesByBaseTravelTime
  101. ==================
  102. */
  103. int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxteammates) {
  104.  
  105.     int i, j, k, numteammates, traveltime;
  106.     char buf[MAX_INFO_STRING];
  107.     static int maxclients;
  108.     int traveltimes[MAX_CLIENTS];
  109.     bot_goal_t *goal;
  110.  
  111.     if (BotCTFTeam(bs) == CTF_TEAM_RED) goal = &ctf_redflag;
  112.     else goal = &ctf_blueflag;
  113.  
  114.     if (!maxclients)
  115.         maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
  116.  
  117.     numteammates = 0;
  118.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  119.         trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
  120.         //if no config string or no name
  121.         if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
  122.         //skip spectators
  123.         if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
  124.         //
  125.         if (BotSameTeam(bs, i)) {
  126.             //
  127.             traveltime = BotClientTravelTimeToGoal(i, goal);
  128.             //
  129.             for (j = 0; j < numteammates; j++) {
  130.                 if (traveltime < traveltimes[j]) {
  131.                     for (k = numteammates; k > j; k--) {
  132.                         traveltimes[k] = traveltimes[k-1];
  133.                         teammates[k] = teammates[k-1];
  134.                     }
  135.                     traveltimes[j] = traveltime;
  136.                     teammates[j] = i;
  137.                     break;
  138.                 }
  139.             }
  140.             if (j >= numteammates) {
  141.                 traveltimes[j] = traveltime;
  142.                 teammates[j] = i;
  143.             }
  144.             numteammates++;
  145.             if (numteammates >= maxteammates) break;
  146.         }
  147.     }
  148.     return numteammates;
  149. }
  150.  
  151. /*
  152. ==================
  153. BotGetTeamMateCTFPreference
  154. ==================
  155. */
  156. void BotSetTeamMateCTFPreference(bot_state_t *bs, int teammate, int preference) {
  157.     char teammatename[MAX_NETNAME];
  158.  
  159.     ctftaskpreferences[teammate].preference = preference;
  160.     ClientName(teammate, teammatename, sizeof(teammatename));
  161.     strcpy(ctftaskpreferences[teammate].name, teammatename);
  162. }
  163.  
  164. /*
  165. ==================
  166. BotGetTeamMateCTFPreference
  167. ==================
  168. */
  169. int BotGetTeamMateCTFPreference(bot_state_t *bs, int teammate) {
  170.     char teammatename[MAX_NETNAME];
  171.  
  172.     if (!ctftaskpreferences[teammate].preference) return 0;
  173.     ClientName(teammate, teammatename, sizeof(teammatename));
  174.     if (Q_stricmp(teammatename, ctftaskpreferences[teammate].name)) return 0;
  175.     return ctftaskpreferences[teammate].preference;
  176. }
  177.  
  178. /*
  179. ==================
  180. BotSortTeamMatesByCTFPreference
  181. ==================
  182. */
  183. int BotSortTeamMatesByCTFPreference(bot_state_t *bs, int *teammates, int numteammates) {
  184.     int defenders[MAX_CLIENTS], numdefenders;
  185.     int attackers[MAX_CLIENTS], numattackers;
  186.     int roamers[MAX_CLIENTS], numroamers;
  187.     int i, preference;
  188.  
  189.     numdefenders = numattackers = numroamers = 0;
  190.     for (i = 0; i < numteammates; i++) {
  191.         preference = BotGetTeamMateCTFPreference(bs, teammates[i]);
  192.         if (preference & CTFTP_DEFENDER) {
  193.             defenders[numdefenders++] = teammates[i];
  194.         }
  195.         else if (preference & CTFTP_ATTACKER) {
  196.             attackers[numattackers++] = teammates[i];
  197.         }
  198.         else {
  199.             roamers[numroamers++] = teammates[i];
  200.         }
  201.     }
  202.     numteammates = 0;
  203.     //defenders at the front of the list
  204.     memcpy(&teammates[numteammates], defenders, numdefenders);
  205.     numteammates += numdefenders;
  206.     //roamers in the middle
  207.     memcpy(&teammates[numteammates], roamers, numroamers);
  208.     numteammates += numroamers;
  209.     //attacker in the back of the list
  210.     memcpy(&teammates[numteammates], attackers, numattackers);
  211.     numteammates += numattackers;
  212.  
  213.     return numteammates;
  214. }
  215.  
  216. /*
  217. ==================
  218. BotSayTeamOrders
  219. ==================
  220. */
  221. void BotSayTeamOrder(bot_state_t *bs, int toclient) {
  222.     char teamchat[MAX_MESSAGE_SIZE];
  223.     char buf[MAX_MESSAGE_SIZE];
  224.     char name[MAX_NETNAME];
  225.  
  226.     //if the bot is talking to itself
  227.     if (bs->client == toclient) {
  228.         //don't show the message just put it in the console message queue
  229.         trap_BotGetChatMessage(bs->cs, buf, sizeof(buf));
  230.         ClientName(bs->client, name, sizeof(name));
  231.         Com_sprintf(teamchat, sizeof(teamchat), "(%s): %s", name, buf);
  232.         trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, teamchat);
  233.     }
  234.     else {
  235.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  236.     }
  237. }
  238.  
  239. /*
  240. ==================
  241. BotCTFOrders
  242. ==================
  243. */
  244. void BotCTFOrders_BothFlagsNotAtBase(bot_state_t *bs) {
  245.     int numteammates, defenders, attackers, i, other;
  246.     int teammates[MAX_CLIENTS];
  247.     char name[MAX_NETNAME], carriername[MAX_NETNAME];
  248.  
  249.     numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates));
  250.     BotSortTeamMatesByCTFPreference(bs, teammates, numteammates);
  251.     //different orders based on the number of team mates
  252.     switch(bs->numteammates) {
  253.         case 1: break;
  254.         case 2:
  255.         {
  256.             //tell the one not carrying the flag to attack the enemy base
  257.             if (teammates[0] != bs->flagcarrier) other = teammates[0];
  258.             else other = teammates[1];
  259.             ClientName(other, name, sizeof(name));
  260.             BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  261.             BotSayTeamOrder(bs, other);
  262.             break;
  263.         }
  264.         case 3:
  265.         {
  266.             //tell the one closest to the base not carrying the flag to accompany the flag carrier
  267.             if (teammates[0] != bs->flagcarrier) other = teammates[0];
  268.             else other = teammates[1];
  269.             ClientName(other, name, sizeof(name));
  270.             ClientName(bs->flagcarrier, carriername, sizeof(carriername));
  271.             if (bs->flagcarrier == bs->client) {
  272.                 BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL);
  273.             }
  274.             else {
  275.                 BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL);
  276.             }
  277.             BotSayTeamOrder(bs, other);
  278.             //tell the one furthest from the the base not carrying the flag to get the enemy flag
  279.             if (teammates[2] != bs->flagcarrier) other = teammates[2];
  280.             else other = teammates[1];
  281.             ClientName(other, name, sizeof(name));
  282.             BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  283.             BotSayTeamOrder(bs, other);
  284.             break;
  285.         }
  286.         default:
  287.         {
  288.             defenders = (int) (float) numteammates * 0.4 + 0.5;
  289.             if (defenders > 4) defenders = 4;
  290.             attackers = (int) (float) numteammates * 0.5 + 0.5;
  291.             if (attackers > 5) attackers = 5;
  292.             ClientName(bs->flagcarrier, carriername, sizeof(carriername));
  293.             for (i = 0; i < defenders; i++) {
  294.                 //
  295.                 if (teammates[i] == bs->flagcarrier) {
  296.                     continue;
  297.                 }
  298.                 //
  299.                 ClientName(teammates[i], name, sizeof(name));
  300.                 if (bs->flagcarrier == bs->client) {
  301.                     BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL);
  302.                 }
  303.                 else {
  304.                     BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL);
  305.                 }
  306.                 BotSayTeamOrder(bs, teammates[i]);
  307.             }
  308.             for (i = 0; i < attackers; i++) {
  309.                 //
  310.                 if (teammates[numteammates - i - 1] == bs->flagcarrier) {
  311.                     continue;
  312.                 }
  313.                 //
  314.                 ClientName(teammates[numteammates - i - 1], name, sizeof(name));
  315.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  316.                 BotSayTeamOrder(bs, teammates[numteammates - i - 1]);
  317.             }
  318.             //
  319.             break;
  320.         }
  321.     }
  322. }
  323.  
  324. /*
  325. ==================
  326. BotCTFOrders
  327. ==================
  328. */
  329. void BotCTFOrders_FlagNotAtBase(bot_state_t *bs) {
  330.     int numteammates, defenders, attackers, i;
  331.     int teammates[MAX_CLIENTS];
  332.     char name[MAX_NETNAME];
  333.  
  334.     numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates));
  335.     BotSortTeamMatesByCTFPreference(bs, teammates, numteammates);
  336.     //passive strategy
  337.     if (bs->ctfstrategy & CTFS_PASSIVE)
  338.     {
  339.         //different orders based on the number of team mates
  340.         switch(bs->numteammates) {
  341.             case 1: break;
  342.             case 2:
  343.             {
  344.                 //both will go for the enemy flag
  345.                 ClientName(teammates[0], name, sizeof(name));
  346.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  347.                 BotSayTeamOrder(bs, teammates[0]);
  348.                 //
  349.                 ClientName(teammates[1], name, sizeof(name));
  350.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  351.                 BotSayTeamOrder(bs, teammates[1]);
  352.                 break;
  353.             }
  354.             case 3:
  355.             {
  356.                 //keep one near the base for when the flag is returned
  357.                 ClientName(teammates[0], name, sizeof(name));
  358.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  359.                 BotSayTeamOrder(bs, teammates[0]);
  360.                 //the other two get the flag
  361.                 ClientName(teammates[1], name, sizeof(name));
  362.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  363.                 BotSayTeamOrder(bs, teammates[1]);
  364.                 //
  365.                 ClientName(teammates[2], name, sizeof(name));
  366.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  367.                 BotSayTeamOrder(bs, teammates[2]);
  368.                 break;
  369.             }
  370.             default:
  371.             {
  372.                 //keep some people near the base for when the flag is returned
  373.                 defenders = (int) (float) numteammates * 0.3 + 0.5;
  374.                 if (defenders > 2) defenders = 2;
  375.                 attackers = (int) (float) numteammates * 0.7 + 0.5;
  376.                 if (attackers > 5) attackers = 5;
  377.                 for (i = 0; i < defenders; i++) {
  378.                     //
  379.                     ClientName(teammates[i], name, sizeof(name));
  380.                     BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  381.                     BotSayTeamOrder(bs, teammates[i]);
  382.                 }
  383.                 for (i = 0; i < attackers; i++) {
  384.                     //
  385.                     ClientName(teammates[numteammates - i - 1], name, sizeof(name));
  386.                     BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  387.                     BotSayTeamOrder(bs, teammates[numteammates - i - 1]);
  388.                 }
  389.                 //
  390.                 break;
  391.             }
  392.         }
  393.     }
  394.     //agressive
  395.     else {
  396.         //different orders based on the number of team mates
  397.         switch(bs->numteammates) {
  398.             case 1: break;
  399.             case 2:
  400.             {
  401.                 //both will go for the enemy flag
  402.                 ClientName(teammates[0], name, sizeof(name));
  403.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  404.                 BotSayTeamOrder(bs, teammates[0]);
  405.                 //
  406.                 ClientName(teammates[1], name, sizeof(name));
  407.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  408.                 BotSayTeamOrder(bs, teammates[1]);
  409.                 break;
  410.             }
  411.             case 3:
  412.             {
  413.                 //everyone go for the flag
  414.                 ClientName(teammates[0], name, sizeof(name));
  415.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  416.                 BotSayTeamOrder(bs, teammates[0]);
  417.                 //
  418.                 ClientName(teammates[1], name, sizeof(name));
  419.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  420.                 BotSayTeamOrder(bs, teammates[1]);
  421.                 //
  422.                 ClientName(teammates[2], name, sizeof(name));
  423.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  424.                 BotSayTeamOrder(bs, teammates[2]);
  425.                 break;
  426.             }
  427.             default:
  428.             {
  429.                 //keep some people near the base for when the flag is returned
  430.                 defenders = (int) (float) numteammates * 0.2 + 0.5;
  431.                 if (defenders > 2) defenders = 2;
  432.                 attackers = (int) (float) numteammates * 0.7 + 0.5;
  433.                 if (attackers > 5) attackers = 5;
  434.                 for (i = 0; i < defenders; i++) {
  435.                     //
  436.                     ClientName(teammates[i], name, sizeof(name));
  437.                     BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  438.                     BotSayTeamOrder(bs, teammates[i]);
  439.                 }
  440.                 for (i = 0; i < attackers; i++) {
  441.                     //
  442.                     ClientName(teammates[numteammates - i - 1], name, sizeof(name));
  443.                     BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  444.                     BotSayTeamOrder(bs, teammates[numteammates - i - 1]);
  445.                 }
  446.                 //
  447.                 break;
  448.             }
  449.         }
  450.     }
  451. }
  452.  
  453. /*
  454. ==================
  455. BotCTFOrders
  456. ==================
  457. */
  458. void BotCTFOrders_EnemyFlagNotAtBase(bot_state_t *bs) {
  459.     int numteammates, defenders, attackers, i, other;
  460.     int teammates[MAX_CLIENTS];
  461.     char name[MAX_NETNAME], carriername[MAX_NETNAME];
  462.  
  463.     numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates));
  464.     BotSortTeamMatesByCTFPreference(bs, teammates, numteammates);
  465.     //different orders based on the number of team mates
  466.     switch(numteammates) {
  467.         case 1: break;
  468.         case 2:
  469.         {
  470.             //tell the one not carrying the flag to defend the base
  471.             if (teammates[0] == bs->flagcarrier) other = teammates[1];
  472.             else other = teammates[0];
  473.             ClientName(other, name, sizeof(name));
  474.             BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  475.             BotSayTeamOrder(bs, other);
  476.             break;
  477.         }
  478.         case 3:
  479.         {
  480.             //tell the one closest to the base not carrying the flag to defend the base
  481.             if (teammates[0] != bs->flagcarrier) other = teammates[0];
  482.             else other = teammates[1];
  483.             ClientName(other, name, sizeof(name));
  484.             BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  485.             BotSayTeamOrder(bs, other);
  486.             //tell the one furthest from the base not carrying the flag to accompany the flag carrier
  487.             if (teammates[2] != bs->flagcarrier) other = teammates[2];
  488.             else other = teammates[1];
  489.             ClientName(other, name, sizeof(name));
  490.             ClientName(bs->flagcarrier, carriername, sizeof(carriername));
  491.             if (bs->flagcarrier == bs->client) {
  492.                 BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL);
  493.             }
  494.             else {
  495.                 BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL);
  496.             }
  497.             BotSayTeamOrder(bs, other);
  498.             break;
  499.         }
  500.         default:
  501.         {
  502.             //40% will defend the base
  503.             defenders = (int) (float) numteammates * 0.4 + 0.5;
  504.             if (defenders > 4) defenders = 4;
  505.             //50% accompanies the flag carrier
  506.             attackers = (int) (float) numteammates * 0.5 + 0.5;
  507.             if (attackers > 5) attackers = 5;
  508.             for (i = 0; i < defenders; i++) {
  509.                 //
  510.                 if (teammates[i] == bs->flagcarrier) {
  511.                     continue;
  512.                 }
  513.                 ClientName(teammates[i], name, sizeof(name));
  514.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  515.                 BotSayTeamOrder(bs, teammates[i]);
  516.             }
  517.             ClientName(bs->flagcarrier, carriername, sizeof(carriername));
  518.             for (i = 0; i < attackers; i++) {
  519.                 //
  520.                 if (teammates[numteammates - i - 1] == bs->flagcarrier) {
  521.                     continue;
  522.                 }
  523.                 //
  524.                 ClientName(teammates[numteammates - i - 1], name, sizeof(name));
  525.                 if (bs->flagcarrier == bs->client) {
  526.                     BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL);
  527.                 }
  528.                 else {
  529.                     BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL);
  530.                 }
  531.                 BotSayTeamOrder(bs, teammates[numteammates - i - 1]);
  532.             }
  533.             //
  534.             break;
  535.         }
  536.     }
  537. }
  538.  
  539.  
  540. /*
  541. ==================
  542. BotCTFOrders
  543. ==================
  544. */
  545. void BotCTFOrders_BothFlagsAtBase(bot_state_t *bs) {
  546.     int numteammates, defenders, attackers, i;
  547.     int teammates[MAX_CLIENTS];
  548.     char name[MAX_NETNAME];
  549.  
  550.     //sort team mates by travel time to base
  551.     numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates));
  552.     //sort team mates by CTF preference
  553.     BotSortTeamMatesByCTFPreference(bs, teammates, numteammates);
  554.     //passive strategy
  555.     if (bs->ctfstrategy & CTFS_PASSIVE)
  556.     {
  557.         //different orders based on the number of team mates
  558.         switch(numteammates) {
  559.             case 1: break;
  560.             case 2:
  561.             {
  562.                 //the one closest to the base will defend the base
  563.                 ClientName(teammates[0], name, sizeof(name));
  564.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  565.                 BotSayTeamOrder(bs, teammates[0]);
  566.                 //the other will get the flag
  567.                 ClientName(teammates[1], name, sizeof(name));
  568.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  569.                 BotSayTeamOrder(bs, teammates[1]);
  570.                 break;
  571.             }
  572.             case 3:
  573.             {
  574.                 //the one closest to the base will defend the base
  575.                 ClientName(teammates[0], name, sizeof(name));
  576.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  577.                 BotSayTeamOrder(bs, teammates[0]);
  578.                 //the second one closest to the base will defend the base
  579.                 ClientName(teammates[1], name, sizeof(name));
  580.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  581.                 BotSayTeamOrder(bs, teammates[1]);
  582.                 //the other will get the flag
  583.                 ClientName(teammates[2], name, sizeof(name));
  584.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  585.                 BotSayTeamOrder(bs, teammates[2]);
  586.                 break;
  587.             }
  588.             default:
  589.             {
  590.                 defenders = (int) (float) numteammates * 0.5 + 0.5;
  591.                 if (defenders > 5) defenders = 5;
  592.                 attackers = (int) (float) numteammates * 0.4 + 0.5;
  593.                 if (attackers > 4) attackers = 4;
  594.                 for (i = 0; i < defenders; i++) {
  595.                     //
  596.                     ClientName(teammates[i], name, sizeof(name));
  597.                     BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  598.                     BotSayTeamOrder(bs, teammates[i]);
  599.                 }
  600.                 for (i = 0; i < attackers; i++) {
  601.                     //
  602.                     ClientName(teammates[numteammates - i - 1], name, sizeof(name));
  603.                     BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  604.                     BotSayTeamOrder(bs, teammates[numteammates - i - 1]);
  605.                 }
  606.                 //
  607.                 break;
  608.             }
  609.         }
  610.     }
  611.     else { //agressive
  612.         //different orders based on the number of team mates
  613.         switch(numteammates) {
  614.             case 1: break;
  615.             case 2:
  616.             {
  617.                 //the one closest to the base will defend the base
  618.                 ClientName(teammates[0], name, sizeof(name));
  619.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  620.                 BotSayTeamOrder(bs, teammates[0]);
  621.                 //the other will get the flag
  622.                 ClientName(teammates[1], name, sizeof(name));
  623.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  624.                 BotSayTeamOrder(bs, teammates[1]);
  625.                 break;
  626.             }
  627.             case 3:
  628.             {
  629.                 //the one closest to the base will defend the base
  630.                 ClientName(teammates[0], name, sizeof(name));
  631.                 BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  632.                 BotSayTeamOrder(bs, teammates[0]);
  633.                 //the others should go for the enemy flag
  634.                 ClientName(teammates[1], name, sizeof(name));
  635.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  636.                 BotSayTeamOrder(bs, teammates[1]);
  637.                 //
  638.                 ClientName(teammates[2], name, sizeof(name));
  639.                 BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  640.                 BotSayTeamOrder(bs, teammates[2]);
  641.                 break;
  642.             }
  643.             default:
  644.             {
  645.                 defenders = (int) (float) numteammates * 0.4 + 0.5;
  646.                 if (defenders > 4) defenders = 4;
  647.                 attackers = (int) (float) numteammates * 0.5 + 0.5;
  648.                 if (attackers > 5) attackers = 5;
  649.                 for (i = 0; i < defenders; i++) {
  650.                     //
  651.                     ClientName(teammates[i], name, sizeof(name));
  652.                     BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL);
  653.                     BotSayTeamOrder(bs, teammates[i]);
  654.                 }
  655.                 for (i = 0; i < attackers; i++) {
  656.                     //
  657.                     ClientName(teammates[numteammates - i - 1], name, sizeof(name));
  658.                     BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL);
  659.                     BotSayTeamOrder(bs, teammates[numteammates - i - 1]);
  660.                 }
  661.                 //
  662.                 break;
  663.             }
  664.         }
  665.     }
  666. }
  667.  
  668.  
  669. /*
  670. ==================
  671. BotTeamOrders
  672. ==================
  673. */
  674. void BotTeamOrders(bot_state_t *bs) {
  675.     //no teamplay orders at this time
  676. }
  677.  
  678.  
  679. /*
  680. ==================
  681. BotTeamAI
  682. ==================
  683. */
  684. void BotTeamAI(bot_state_t *bs) {
  685.     int numteammates, flagstatus;
  686.     char netname[MAX_NETNAME];
  687.  
  688.     //
  689.     if (gametype != GT_TEAM && gametype != GT_CTF) return;
  690.     //make sure we've got a valid team leader
  691.     if (!BotValidTeamLeader(bs)) {
  692.         //
  693.         if (!bs->askteamleader_time && !bs->becometeamleader_time) {
  694.             if (bs->entergame_time + 10 > trap_AAS_Time()) {
  695.                 bs->askteamleader_time = trap_AAS_Time() + 5 + random() * 10;
  696.             }
  697.             else {
  698.                 bs->becometeamleader_time = trap_AAS_Time() + 5 + random() * 10;
  699.             }
  700.         }
  701.         if (bs->askteamleader_time && bs->askteamleader_time < trap_AAS_Time()) {
  702.             //if asked for a team leader and no repsonse
  703.             BotAI_BotInitialChat(bs, "whoisteamleader", NULL);
  704.             trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  705.             bs->askteamleader_time = 0;
  706.             bs->becometeamleader_time = trap_AAS_Time() + 15 + random() * 10;
  707.         }
  708.         if (bs->becometeamleader_time && bs->becometeamleader_time < trap_AAS_Time()) {
  709.             BotAI_BotInitialChat(bs, "iamteamleader", NULL);
  710.             trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  711.             ClientName(bs->client, netname, sizeof(netname));
  712.             strncpy(bs->teamleader, netname, sizeof(bs->teamleader));
  713.             bs->teamleader[sizeof(bs->teamleader)] = '\0';
  714.             bs->becometeamleader_time = 0;
  715.         }
  716.         return;
  717.     }
  718.     bs->askteamleader_time = 0;
  719.     bs->becometeamleader_time = 0;
  720.  
  721.     //return if this bot is NOT the team leader
  722.     ClientName(bs->client, netname, sizeof(netname));
  723.     if (Q_stricmp(netname, bs->teamleader) != 0) return;
  724.     //
  725.     //if the game starts OR a new player comes onto the team OR a player leaves the team
  726.     //
  727.     numteammates = BotNumTeamMates(bs);
  728.     //give orders
  729.     switch(gametype) {
  730.         case GT_TEAM:
  731.         {
  732.             if (bs->numteammates != numteammates || bs->forceorders) {
  733.                 bs->teamgiveorders_time = trap_AAS_Time();
  734.                 bs->numteammates = numteammates;
  735.                 bs->forceorders = qfalse;
  736.             }
  737.             //if it's time to give orders
  738.             if (bs->teamgiveorders_time < trap_AAS_Time() - 5) {
  739.                 BotTeamOrders(bs);
  740.                 //
  741.                 bs->teamgiveorders_time = 0;
  742.             }
  743.             break;
  744.         }
  745.         case GT_CTF:
  746.         {
  747.             //if the number of team mates changed or the flag status changed
  748.             //or someone wants to know what to do
  749.             if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders) {
  750.                 bs->teamgiveorders_time = trap_AAS_Time();
  751.                 bs->numteammates = numteammates;
  752.                 bs->flagstatuschanged = qfalse;
  753.                 bs->forceorders = qfalse;
  754.             }
  755.             //if there were no flag captures the last 3 minutes
  756.             if (bs->lastflagcapture_time < trap_AAS_Time() - 240) {
  757.                 bs->lastflagcapture_time = trap_AAS_Time();
  758.                 //randomly change the CTF strategy
  759.                 if (random() < 0.4) {
  760.                     bs->ctfstrategy ^= CTFS_PASSIVE;
  761.                     bs->teamgiveorders_time = trap_AAS_Time();
  762.                 }
  763.             }
  764.             //if it's time to give orders
  765.             if (bs->teamgiveorders_time && bs->teamgiveorders_time < trap_AAS_Time() - 3) {
  766.                 //
  767.                 if (BotCTFTeam(bs) == CTF_TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus;
  768.                 else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus;
  769.                 //
  770.                 switch(flagstatus) {
  771.                     case 0: BotCTFOrders_BothFlagsAtBase(bs); break;
  772.                     case 1: BotCTFOrders_EnemyFlagNotAtBase(bs); break;
  773.                     case 2: BotCTFOrders_FlagNotAtBase(bs); break;
  774.                     case 3: BotCTFOrders_BothFlagsNotAtBase(bs); break;
  775.                 }
  776.                 //
  777.                 bs->teamgiveorders_time = 0;
  778.             }
  779.             break;
  780.         }
  781.     }
  782. }
  783.  
  784.